package anonymous;

import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;

import rescuecore2.worldmodel.EntityID;
import rescuecore2.worldmodel.ChangeSet;
import rescuecore2.messages.Command;
import rescuecore2.log.Logger;
import rescuecore2.misc.geometry.GeometryTools2D;
import rescuecore2.misc.geometry.Point2D;
import rescuecore2.misc.geometry.Line2D;

import rescuecore2.standard.entities.StandardEntity;
import rescuecore2.standard.entities.StandardEntityURN;
import rescuecore2.standard.entities.Road;
import rescuecore2.standard.entities.Blockade;
import rescuecore2.standard.entities.PoliceForce;
import rescuecore2.standard.entities.Area;

/**
  A sample police force agent.
  */
public class AnonymousPoliceForce extends AbstractAnonymousAgent<PoliceForce> {
	private static final String DISTANCE_KEY = "clear.repair.distance";

	private int distance;

	@Override
		protected boolean iAmPolice(){
			return true;
		}

	@Override
		public String toString() {
			return "Anonymous police force";
		}

	@Override
		protected void postConnect() {
			super.postConnect();
			model.indexClass(StandardEntityURN.ROAD);
			distance = config.getIntValue(DISTANCE_KEY);
		}
	@Override
	protected void startup(int time){};
	@Override
	protected void task(int time){
		if(!this.hasTask()){
			this.current_state = State.PATROL;
			patrol(time);
		}
		else{

			int i=0;
			//get task
			Task current_task = taskList.top(model);
			do{
				current_task = taskList.top(model);
				if(model.getEntity(current_task.eID) == null){
					taskList.remove(current_task);
					current_task = null;
				}
			}while( !taskList.isEmpty() && (current_task == null));

			taskList.print();
			Logger.debug("Current task id: "+current_task.eID);
			Logger.debug("Current task entity: "+model.getEntity(current_task.eID));
			//TODO: Review logic
			//if has no path, get the best task that has a path to it
			List<EntityID> path = search.breadthFirstSearch(me().getPosition(), (current_task.area).getNeighbours());
			Logger.debug("Valdinho -- Estou em "+location().getID()+" e vou para " +current_task.area.getID());
			Logger.debug("Valdinho -- Task area: " + current_task.area);
			Logger.debug("distancia para desbloquear: "+ (findDistanceTo((Blockade)model.getEntity(current_task.eID),me().getX(), me().getY()) < distance));
			updateTasks();

			//if on any barricade, clean it
			EntityID b = getNearestBlockade();
			if( findDistanceTo((Blockade)model.getEntity(b), me().getX(), me().getY()) < (distance)){
				Logger.debug("Valdinho -- Desbloqueando um próximo");
				Logger.debug("Valdinho -- Distance é: "+distance);
				taskList.remove(b);
				sendClear(time, b);
				return;
			}
			else{
				Logger.debug("Valdinho -- Não é perto o suficiente");
			}


			if(findDistanceTo((Blockade)model.getEntity(current_task.eID),me().getX(), me().getY()) < (distance)){
				Logger.debug("Valdinho -- Desbloqueando");
				taskList.remove(b);
				sendClear(time, current_task.eID);
				return;
			}

			//moving to the target barricade
			if(path != null){
				//go to blockade
				Logger.debug("TANNOS - MOVING_A - PATH: "+path+" in time "+time);
				Logger.debug("Valdinho -- Estou em: "+location()+", "+location().getLocation(model));
				Logger.debug("Valdinho -- Indo para "+current_task.location);
				path.add(current_task.area.getID());
				sendMove(time, path, current_task.location.first(), current_task.location.second());
			}
			else{
				Logger.debug("TANNOS - BLOCKED_A");
				Logger.debug("Valdinho -- ISSO NUNCA SERÁ IMPRESSO");
			}
		}
	}

	private EntityID getNearestBlockade(){
		int min = 999999999; //lots of nines
		EntityID b = null;
		for(EntityID eid : getLocalBlockedRoads()){
			int d = findDistanceTo((Blockade)model.getEntity(eid),me().getX(), me().getY());
			if(d < min){
				min = d;
				b = eid;
			}
		}
		return b;
	}

	private ArrayList<EntityID> getLocalBlockedRoads() {
		Collection<StandardEntity> e = model.getEntitiesOfType(StandardEntityURN.ROAD);
		ArrayList<EntityID> result = new ArrayList<EntityID>();
		for (StandardEntity next : e) {
			Road r = (Road)next;
			if (r.isBlockadesDefined() && !r.getBlockades().isEmpty()) {
				for(EntityID b : r.getBlockades()){
					result.add(b);
				}
			}
		}
		return result;
	}

	private int findDistanceTo(Blockade b, int x, int y) {
		//        Logger.debug("Finding distance to " + b + " from " + x + ", " + y);
		List<Line2D> lines = GeometryTools2D.pointsToLines(GeometryTools2D.vertexArrayToPoints(b.getApexes()), true);
		double best = Double.MAX_VALUE;
		Point2D origin = new Point2D(x, y);
		for (Line2D next : lines) {
			Point2D closest = GeometryTools2D.getClosestPointOnSegment(next, origin);
			double d = GeometryTools2D.getDistance(origin, closest);
			//            Logger.debug("Next line: " + next + ", closest point: " + closest + ", distance: " + d);
			if (d < best) {
				best = d;
				//                Logger.debug("New best distance");
			}

		}
		return (int)best;
	}

	private void updateTasks(){
		for(StandardEntity b: model.getEntitiesOfType(StandardEntityURN.ROAD)){
			for(Task t : taskList.getList()){
				if(t.eID == b.getID())
					taskList.remove(t);
			}
		}
	}


	@Override
	protected void patrol(int time){
		//TODO: Improve patrol
		if(this.hasTask()){
			Logger.debug("Valdinho -- Achei uma tarefa");
			this.current_state = State.TASK;
			task(time);
		}
		else{
			Logger.debug("Valdinho -- Procurando uma tarefa");
			List<EntityID> path = randomWalk();
			sendMove(time, path);
		}
	}
	@Override
	protected void displacement(int time){};

	@Override
		protected EnumSet<StandardEntityURN> getRequestedEntityURNsEnum() {
			return EnumSet.of(StandardEntityURN.POLICE_FORCE);
		}
}
